from zope.interface import implements

import time

from twisted.internet import defer, reactor

from pyhsrv.interfaces import IPollerService
from pyhsrv import errors

status_id = ['online','unreachable','inactive']

#Server-Node polling:

class PollingService(object):
    """Generates the queries, and stores the answers in db"""
    
    implements(IPollerService)
    
    def __init__(self, db, sname, nodeid):
        self.db = db
        self.servername = sname
        self.nodeid = nodeid
        self.newplugins = []
        self.splugins = {}
        self.bplugins = {}
        self.init_done = False
        def fillplugins(plugs):
            for i in plugs:
                k = {}
                k['delay'] = i.delay
                k['buffered'] = i.buffered
                k['last'] = i.lasttime
                if k['buffered'] == 0:
                    self.splugins[i.name] = k
                else:
                    self.bplugins[i.name] = k
            self.init_done = True
        d = self.db.getPlugins(self.nodeid, session=True)
        d.addCallback(fillplugins)
        d.addErrback(errors.catchError)

    def setProperty(self, params):
        if params[0] in self.bplugins.keys() + self.splugins.keys():
            if params[1] == "period":
                if params[0] in self.splugins.keys():
                    self.splugins[params[0]]['delay'] = params[2]
                else:
                    self.bplugins[params[0]]['delay'] = params[2]
                    self.sendfv("set " + " ".join(params))
            elif len(params[2].split(':')) == 2:
                self.sendfv("set " + " ".join(params))

    def updateStatus(self, status):
        self.db.updateNodeStatus(self.nodeid, status_id.index(status),
                                 session=True)
        
    def getAddr(self):
        def return_addr(node):
            return [node.addr, node.port]
        d = self.db.getNode_by_id(self.nodeid, session=True)
        d.addCallback(return_addr)
        d.addErrback(errors.catchError)
        return d
    
    def poller(self, plugin, plugname):
        querystr = "get " if plugin['buffered'] == 0 else "mget "
        delay = int(plugin['delay']) if int(plugin['buffered']) == 0\
              else int(plugin['delay']) * int(plugin['buffered'])
        querystr += plugname
        if plugin['buffered'] > 0:
            querystr += " " + str(int(plugin['last'] - self.deltat))
        self.sendfv(querystr)
        reactor.callLater(delay, self.poller, plugin, plugname)

    def startPolling(self, x):
        while not self.init_done:
            pass
        for i in self.splugins.keys():
            self.poller(self.splugins[i], i)
        for i in self.bplugins.keys():
            self.poller(self.bplugins[i], i)

    def parseMsg(self, msg):
        cmd, params = msg.split(' ')[0], msg.split(' ')[1:]
        
        if cmd == "splugins":
            if len(params) == 1:
                for i in params[0].split(','):
                    if not self.splugins.has_key(i):
                        if params[0] != '':
                            self.newplugins.append(i)
            return defer.succeed("getbplugins")
        
        elif cmd == "bplugins":
            if len(params) == 1:
                for i in params[0].split(','):
                    if not self.bplugins.has_key(i):
                        if params[0] != '':
                            self.newplugins.append(i)
                        
            if len(self.newplugins) == 0:
                self.startPolling("")
                return defer.succeed(None)
            else:
                return defer.succeed("getconfig " + self.newplugins.pop())
        
        elif cmd == "config":
            if len(params) == 2:
                pl = {}
                plug_param = params[1].split('|')[0]
                item_param = params[1].split('|')[1:]
                for param in plug_param.split(';'):
                    i = param.split(':')
                    i[1] = ' '.join(i[1].split('@'))
                    if i[0] == "buffered":
                        if i[1] == "true":
                            pl[i[0]] = 5
                            pl['last'] = 0
                        else:
                            pl[i[0]] = 0
                    else:
                        pl[i[0]] = i[1]
                d = self.db.insertPlugin(self.nodeid, params[0], pl,
                                         session=True)
                if pl['buffered'] == 0:
                    self.splugins[params[0]] = pl
                else:
                    self.bplugins[params[0]] = pl
                def dotheitems(x):
                    for item in item_param:
                        it = {}
                        itname = item.split('#')[0]
                        itparam = item.split('#')[1]
                        for itp in itparam.split(';'):
                            i = itp.split(':')
                            i[1] = ' '.join(i[1].split('@'))
                            it[i[0]] = i[1]
                        d = self.db.insertItem(self.nodeid, params[0], itname,
                                               it, session=True)
                    return d
                d.addCallback(dotheitems)

                if len(self.newplugins) == 0:
                    d.addCallback(self.startPolling)
                    d.addErrback(errors.catchError)
                    return defer.succeed(None)
                else:
                    d.addErrback(errors.catchError)
                    return defer.succeed("getconfig " + self.newplugins.pop())
        
        elif cmd == "error":
            print msg
            return defer.succeed(None)
        
        elif msg == "you are?":
            return defer.succeed("i'm " + self.servername)
        
        elif cmd == "hello," and len(params) == 2:
            if params[0] == "it's":
                self.deltat = time.time() - float(params[1])
                return defer.succeed("getsplugins")
            return defer.succeed(None)
        
        elif cmd in self.splugins.keys():
            if len(params) == 1:
                tmstmp = int(float(params[0].split('|')[0]) + self.deltat)
                for i in params[0].split('|')[1].split(','):
                    it = i.split(':')[0]
                    val = i.split(':')[1]
                    d1 = self.db.insertValue(self.nodeid, cmd, it, tmstmp,
                                             val, session=True)
                    d1.addErrback(errors.catchError)
                d2 = self.db.updatePluginLast(self.nodeid, cmd, tmstmp,
                                              session=True)
                d2.addErrback(errors.catchError)
            return defer.succeed(None)
        
        elif cmd in self.bplugins.keys():
            if len(params) == 1:
                for vt in params[0].split('#'):
                    tmstmp = int(float(vt.split('|')[0]) + self.deltat)
                    for i in vt.split('|')[1].split(','):
                        it = i.split(':')[0]
                        val = i.split(':')[1]
                        d1 = self.db.insertValue(self.nodeid, cmd, it, tmstmp,
                                                 val, session=True)
                        d1.addErrback(errors.catchError)
                    last = tmstmp
                self.bplugins[cmd]['last'] = last
                d2 = self.db.updatePluginLast(self.nodeid, cmd, last,
                                              session=True)
                d2.addErrback(errors.catchError)
                return defer.succeed("ok " + cmd)
            return defer.succeed(None)
        
        else:
            return defer.succeed(None)
    
    def newConnection(self, sendfv):
        self.sendfv = sendfv
        def format_msg(node):
            return "hello " + node.name
        d = self.db.getNode_by_id(self.nodeid, session=True)
        d.addCallback(format_msg)
        d.addErrback(errors.catchError)
        return d
    
    def closeConnection(self):
        pass
    
    
    
#Server-server polling:    
    
class SrvPollService(object):
    """Generates the queries, and stores the answers in db"""
    
    implements(IPollerService)
    
    def __init__(self, db, sname, childid):
        self.db = db
        self.servername = sname
        self.childid = childid
        self.nodes = {}
        self.newnodes = []
        self.newplugins = {}
        self.init_done = False
        
        def fillNodes(nodes):
            self.nodes = nodes
            self.init_done = True
                
        d = self.db.getNodes_by_srvid(self.childid, session=True)
        d.addCallback(fillNodes)
        d.addErrback(errors.catchError)
        
    def updateStatus(self, status):
        self.db.updateServerStatus(self.childid, status_id.index(status),
                                 session=True)
        
    def getAddr(self):
        def return_addr(server):
            return [server.addr, server.port]
        d = self.db.getServer_by_id(self.childid, session=True)
        d.addCallback(return_addr)
        d.addErrback(errors.catchError)
        return d
    
    def poller(self, nodename, plugname):
        def sendQuery(lasttime):
            querystr = "get " + nodename + " " + plugname + " " + \
                     str(int(lasttime - self.deltat))
            self.sendfv(querystr)
        d = self.db.getLastTime(nodename, plugname, session=True)
        d.addCallback(sendQuery)
        d.addErrback(errors.catchError)
        reactor.callLater(int(self.nodes[nodename][plugname]['delay']),\
                          self.poller, nodename, plugname)

    def startPolling(self, x):
        while not self.init_done:
            pass
        for n in self.nodes.keys():
            for p in self.nodes[n].keys():
                self.poller(n, p)

    def parseMsg(self, msg):
        cmd, params = msg.split(' ')[0], msg.split(' ')[1:]
                
        if cmd == "nodes":
            # format: nodes node1:status1 node2:status2... 
            for i in params:
                name = i.split(':')[0]
                status = i.split(':')[1]
                self.nodes[name] = {}
                self.newnodes.append(name)
                self.newplugins[name] = []
                self.db.addNodeToServer(name, self.childid, status,
                                        session=True)
            
            if len(self.newnodes) == 0:
                self.startPolling('')
                return defer.succeed(None)
            else:
                return defer.succeed("getplugins " + self.newnodes.pop())
        
        elif cmd == "items" and len(params) > 1:
            #format: items nodename:plugname item1:stype,label,cdef,draw,amin,amax
            node = params[0].split(':')[0]
            plug = params[0].split(':')[1]
            if self.nodes.has_key(node):
                if self.nodes[node].has_key(plug):
                    for i in params[1:]:
                        iname = i.split(':')[0]
                        ipar = {}
                        print i
                        ip = i.split(':')[1].split(',')
                        ipar['stype'] = ip[0]
                        if ip[1] != "None":
                            ipar['label'] = ip[1]
                        if ip[2] != "None": 
                            ipar['cdef']  = ip[2]
                        if ip[3] != "None":
                            ipar['draw']  = ip[3]
                        if ip[4] != "None":
                            ipar['amin']  = ip[4]
                        if ip[5] != "None":
                            ipar['amax']  = ip[5]
                        
                        if iname not in self.nodes[node][plug]['items']:
                            self.nodes[node][plug]['items'].append(iname)
                            self.db.insertItem_by_name(node, plug, iname, \
                                                ipar, session=True)
                        
            if len (self.newplugins[node]) == 0:
                if len(self.newnodes) == 0:
                    self.startPolling('')
                    return defer.succeed(None)
                else:
                    return defer.succeed("getplugins " + self.newnodes.pop())
            else:
                return defer.succeed("getitems " + node + " " + \
                                     self.newplugins[node].pop())
        
        elif cmd == "plugins" and len(params) > 1:
            # format: plugins nodename plugin1:vtype,delay,buffered,title ...
            node = params[0]
            if node in self.nodes.keys():
                for p in params[1:]:
                    pname = p.split(':')[0]
                    ppar  = {}
                    pp = p.split(':')[1].split(',')
                    ppar['vtype']    = pp[0]
                    ppar['delay']    = pp[1]
                    ppar['buffered'] = pp[2]
                    ppar['title']    = ' '.join(pp[3].split('|'))
                    
                    if pname not in self.nodes[node].keys():
                        self.nodes[node][pname] = {}
                        self.nodes[node][pname]['delay'] = ppar['delay']\
                            if int(ppar['buffered']) == 0\
                            else int(ppar['delay']) * int(ppar['buffered'])
                        self.nodes[node][pname]['items'] = []
                        self.newplugins[node].append(pname)
                        self.db.insertPlugin_by_name(node, pname, ppar, \
                                                     session=True)
            if len(self.newplugins[node]) == 0:
                if len(self.newnodes) == 0:
                    self.startPolling('')
                    return defer.succeed(None)
                else:
                    return defer.succeed("getplugins " + self.newnodes.pop())
            else:
                return defer.succeed("getitems " + node + " " + \
                                     self.newplugins[node].pop())
            
        #message format:
        #values nodename:status plugin
        #item1:val,time;val,time;...
        elif cmd == "values":
            node = params[0].split(':')[0]
            plugin = params[1].split(':')[0]
            last = 0
            if self.nodes.has_key(unicode(node)):
                if self.nodes[node].has_key(plugin):
                    for itm in params[2].split('|'):
                        if len(itm.split(':')) == 2:
                            itname = itm.split(':')[0]
                            it_t_and_v = itm.split(':')[1]
                            for t_and_v in it_t_and_v.split(';'):
                                tstmp = int(t_and_v.split(',')[1]) + self.deltat
                                if last < int(tstmp):
                                    last = int(tstmp)
                                if self.nodes[node][plugin] < int(tstmp):
                                    self.nodes[node][plugin] = int(tstmp)
                                val = t_and_v.split(',')[0]
                                self.db.insertValue_by_name(node, plugin, itname,
                                                        tstmp, val, session=True)
                    self.db.updatePluginLast_by_name(node, plugin, last,
                                                session=True)
                #self.db.updateNodeStatus_by_name(node, params[0].split(':')[1])
                return defer.succeed(None)
                    
        elif cmd == "error":
            print msg
            return defer.succeed(None)
        
        elif msg == "you are?":
            return defer.succeed("i'm " + self.servername)
        
        elif cmd == "hello," and len(params) == 2:
            if params[0] == "it's":
                self.deltat = time.time() - float(params[1])
                return defer.succeed("getnodes")
            return defer.succeed(None)
        
        else:
            return defer.succeed(None)
    
    def newConnection(self, sendfv):
        self.sendfv = sendfv
        def format_msg(server):
            return "hello " + server.name
        d = self.db.getServer_by_id(self.childid, session=True)
        d.addCallback(format_msg)
        d.addErrback(errors.catchError)
        return d
    
    def closeConnection(self):
        pass
    